#include<iostream>
#include<string>
#include<cstring>
#include<cstdlib>
#include<cerrno>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

#include"logMessage.hpp"

using std::cout;
using std::endl;
using std::string;

class udpServer{
public:
    udpServer(uint16_t port,string ip="")
        :_port(port),
        _ip(ip)
        {}
    ~udpServer()
    {}

    //server init,creat socket,bind host
    void init()
    {
        _sockFd=socket(AF_INET,SOCK_DGRAM,0);
        if(_sockFd<0)
        {
            logMessage(FATAL,"socket() fail::%s :%d",
                    strerror(errno),_sockFd);
            exit(1);
        }
        logMessage(DEBUG,"socket creat success: %d", _sockFd);


        struct sockaddr_in local;
        bzero(&local,sizeof(local));


        local.sin_family=AF_INET;
        local.sin_port=htons(_port);
        local.sin_addr.s_addr=_ip.empty()?htonl(INADDR_ANY):inet_addr(_ip.c_str());


        if(bind(_sockFd,(const struct sockaddr*)&local,sizeof(local))==-1){
            logMessage(FATAL,"bind() failed::%s : %d",
                strerror(errno),_sockFd);
            exit(2);
        }
        logMessage(DEBUG,"socket bind success: %d",_sockFd);
    }

        void start()
        {
            char inBuffer[1024];
            while(1)
            {
                struct sockaddr_in peer;
                socklen_t peerlen=sizeof(peer);

                ssize_t s=recvfrom(_sockFd,inBuffer,sizeof(inBuffer)-1,0,(struct sockaddr*)&peer,&peerlen);
                if(s>0)
                {
                    inBuffer[s]=0;
                }
                else if(s==-1)
                {
                    logMessage(WARINING,"recvfrom() error::%s: %d",strerror(errno),_sockFd);
                    continue;
                }
                string peerIP=inet_ntoa(peer.sin_addr);
                uint32_t peerPort=ntohs(peer.sin_port);

                logMessage(NOTICE,"[%s:%d]# %s",peerIP.c_str(),peerPort,inBuffer);
            }
        }    
private:
    uint16_t _port;
    string _ip;
    int _sockFd;
};
static void Usage(const string porc)
{
    cout<<"Usage:\n\t"<<porc<<"port [ip]"<<endl;
}

int main(int argc,char*argv[])
{
    if(argc != 2&& argc!=3)
    {
        Usage(argv[0]);
        exit(3);
    }
    uint16_t port=atoi(argv[1]);
    string ip;
    if(argc==3)
    {
        ip=argv[2];
    }

    udpServer uSvr(port,ip);
    uSvr.init();
    uSvr.start();
    return 0;
}

